import { Ref, watch } from "vue";

export function useDrag(listRoot: Ref<HTMLElement>, listOption: Ref<any[]>) {
  let activeItem;
  const onDragstart = (e: DragEvent, item: any) => {
    // 借助了一个全局状态activeItem, 所以没有使用setData来传递数据(TODO)
    activeItem = item;
    // 滞后修改样式是因为: 拖拽的瞬间生成拖拽阴影(透明的原dom),生成阴影可见,所以不能把透明度置为完全透明.同时隐藏原位置的dom,修改其样式为完全透明
    setTimeout(() => {
      // 增加样式的元素一定是被拖拽的元素,即触发onDragstart的元素e.target
      (e.target! as HTMLDivElement).classList.add("moveing"); // 通过dom.classList.(add / remove)的方式操作样式只能用e.target获取dom, e.currentTarget获取的dom无法访问classList属性
    }, 0);
  };

  const onDragenter = (_: DragEvent, item: any) => {
    swapItem(item);
  };

  // 交换list数组中activeItem与targetItem
  const swapItem = (targetItem: any) => {
    const activeItemIndex = listOption.value.indexOf(activeItem!);
    const targetItemIndex = listOption.value.indexOf(targetItem);
    listOption.value.splice(activeItemIndex, 1, targetItem);
    listOption.value.splice(targetItemIndex, 1, activeItem!);
  };

  // 拖拽结束
  const onDragend = (e: DragEvent) => {
    (e.target! as HTMLDivElement).classList.remove("moveing");
  };

  const onDragover = (e: DragEvent) => {
    e.preventDefault(); // 默认行为: 拖拽阴影回到原位
  };

  watch(listRoot, () => {
    if (!listRoot.value) return;
    const rootEl = listRoot.value;

    // 子元素设置draggable
    const children = [...(rootEl.children || [])];
    children.forEach((item: Element) => {
      item.setAttribute("draggable", "true");

      item.addEventListener("dragstart", (e) => {
        const lastest_children = [...(rootEl.children || [])]; // 不能直接使用children数组, 因为dragstart的回调函数与watch的回调函数形成闭包, 针对每一次dragstart的回调函数执行来说children数组从未改变
        onDragstart(
          e as DragEvent,
          listOption.value[lastest_children.indexOf(item)]
        );
      });

      item.addEventListener("dragenter", (e) => {
        const lastest_children = [...(rootEl.children || [])];
        onDragenter(
          e as DragEvent,
          listOption.value[lastest_children.indexOf(item)]
        );
      });

      item.addEventListener("dragend", (e) => {
        onDragend(e as DragEvent);
      });

      item.addEventListener("dragover", (e) => {
        onDragover(e as DragEvent);
      });
    });
  });
}

// 拖动列表项并发生交换的过程:
/**
 * 1. 拖拽一个列表项a, 触发a的dragstart事件, 即更新当前的activeItem为a的状态对象
 * 2. 因为本身a也是一个可以接收拖拽项的元素, 所以拖动a的一瞬间也相当于拖着a进入了a, 即触发了a的dragenter事件, dragenter执行的逻辑就是交换列表数组中activeItem与(被enter的元素对应的)本对象 => 列表数组无变化
 * 3. 只要拖动不松手, activeItem一直都是触发dragstart的a元素的状态对象, 此时进入另一个元素b, 触发b的dragenter事件, 在列表数组中交换a的对象与b的对象, vue完成state到ui的渲染即试图效果就是列表顺序变化 => 列表数组中a/b交换位置
 * 4. 列表顺序变化, 原本b的位置变成a, 所以立即触发a元素的dragenter事件 => 列表数组无变化
 */
